// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build amd64 

package runtime

import "unsafe"

const (
	debugCallSystemStack = "executing on Go runtime stack"
	debugCallUnknownFunc = "call from unknown function"
	debugCallRuntime     = "call from within the Go runtime"
	debugCallUnsafePoint = "call not at safe point"
)

func debugCallV2()
func debugCallPanicked(val any)

// debugCallCheck检查注入调试器
// 函数调用并返回PC是否安全。如果不安全，则返回字符串
// 解释原因。
// 
// go:nosplit 
func debugCallCheck(pc uintptr) string {
	// 没有来自系统堆栈的用户调用。
	if getg() != getg().m.curg {
		return debugCallSystemStack
	}
	if sp := getcallersp(); !(getg().stack.lo < sp && sp <= getg().stack.hi) {
		// Fast syscalls（nanotime）和racecall在不切换g的情况下切换到
		// g0堆栈。在这种状态下，我们无法安全地进行
		// a调用。（我们甚至不能安全地
		// systemstack。）
		return debugCallSystemStack
	}

	// 切换到系统堆栈以避免用户溢出
	// 堆栈。
	var ret string
	systemstack(func() {
		f := findfunc(pc)
		if !f.valid() {
			ret = debugCallUnknownFunc
			return
		}

		name := funcname(f)

		switch name {
		case "debugCall32",
			"debugCall64",
			"debugCall128",
			"debugCall256",
			"debugCall512",
			"debugCall1024",
			"debugCall2048",
			"debugCall4096",
			"debugCall8192",
			"debugCall16384",
			"debugCall32768",
			"debugCall65536":
			// 允许使用这些函数，以便调试器可以启动多个函数调用。
			// 请参阅：https:
			return
		}

		// 禁止从运行时调用。我们可以使这种情况变得更严格（例如，当锁被持有时，不是
		// ），但有足够多的加密序列（例如，延迟处理），因此安全性更好。
		if pfx := "runtime."; len(name) > len(pfx) && name[:len(pfx)] == pfx {
			ret = debugCallRuntime
			return
		}

		// 检查这一点是否不安全。
		if pc != f.entry() {
			pc--
		}
		up := pcdatavalue(f, _PCDATA_UnsafePoint, pc, nil)
		if up != _PCDATA_UnsafePointSafe {
			// 不安全。
			ret = debugCallUnsafePoint
		}
	})
	return ret
}

// debugCallWrap启动一个新的goroutine来运行调试调用，并阻止
// 调用goroutine。在goroutine上，它准备从调试调用中恢复
// 恐慌，然后在PC调度中调用调用调度
// 函数。
// 
// 这一定是非常不完整的，因为debugCallV2中的
// 堆栈上存在非类型化的值。
// 
// go:nosplit 
func debugCallWrap(dispatch uintptr) {
	var lockedm bool
	var lockedExt uint32
	callerpc := getcallerpc()
	gp := getg()

	// 创建一个新的goroutine来执行对的调用。在
	// 系统堆栈上运行此命令，以避免堆栈增长。
	systemstack(func() {
		// TODO（mknyszek）：最好将这些参数封装在已分配的
		// 闭包中，并使用该闭包启动goroutine，但编译器不允许在运行时分配
		// 隐式闭包。
		fn := debugCallWrap1
		newg := newproc1(*(**funcval)(unsafe.Pointer(&fn)), gp, callerpc)
		args := &debugCallWrapArgs{
			dispatch: dispatch,
			callingG: gp,
		}
		newg.param = unsafe.Pointer(args)

		// 如果当前G已锁定，则将该
		// 已锁定状态转移到新的goroutine。
		if gp.lockedm != 0 {
			// 保存锁状态以稍后恢复。
			mp := gp.m
			if mp != gp.lockedm.ptr() {
				throw("inconsistent lockedm")
			}

			lockedm = true
			lockedExt = mp.lockedExt

			// 将外部锁计数转移到内部锁计数，因此
			// 无法从调试调用中解锁。
			mp.lockedInt++
			mp.lockedExt = 0

			mp.lockedg.set(newg)
			newg.lockedm.set(mp)
			gp.lockedm = 0
		}

		// 将调用goroutine标记为处于异步
		// 安全点，因为它在堆栈底部有几个保守的帧
		// 。这也可以防止
		// 堆栈收缩。
		gp.asyncSafePoint = true

		// 将newg隐藏起来，以便我们可以在下面执行它（mcall的
		// 闭包无法捕获任何内容）。
		gp.schedlink.set(newg)
	})

	// 切换到新的goroutine。
	mcall(func(gp *g) {
		// 获取新信息。
		newg := gp.schedlink.ptr()
		gp.schedlink = 0

		// 停车呼叫goroutine。
		gp.waitreason = waitReasonDebugCall
		if trace.enabled {
			traceGoPark(traceEvGoBlock, 1)
		}
		casgstatus(gp, _Grunning, _Gwaiting)
		dropg()

		// 直接执行新的goroutine。调试
		// 协议将在新的goroutine上继续，因此
		// 重要的是，我们不能只让调度程序执行
		// 否则它可能会恢复另一个goroutine。
		execute(newg, true)
	})

	// 我们会在这里继续通话。

	// 恢复锁定状态。
	if lockedm {
		mp := gp.m
		mp.lockedExt = lockedExt
		mp.lockedInt--
		mp.lockedg.set(gp)
		gp.lockedm.set(mp)
	}

	gp.asyncSafePoint = false
}

type debugCallWrapArgs struct {
	dispatch uintptr
	callingG *g
}

// debugCallWrap1是被调用方
// goroutine上debugCallWrap1的延续。
func debugCallWrap1() {
	gp := getg()
	args := (*debugCallWrapArgs)(gp.param)
	dispatch, callingG := args.dispatch, args.callingG
	gp.param = nil

	// 调度呼叫和陷阱恐慌。
	debugCallWrap2(dispatch)

	// 继续呼叫goroutine。
	getg().schedlink.set(callingG)
	mcall(func(gp *g) {
		callingG := gp.schedlink.ptr()
		gp.schedlink = 0

		// 如有必要，从M解锁此goroutine。
		// 呼叫G将重新锁定。
		if gp.lockedm != 0 {
			gp.lockedm = 0
			gp.m.lockedg = 0
		}

		// 切换回呼叫goroutine。在某个时刻，调度程序将再次安排我们，我们将完成退出。
		if trace.enabled {
			traceGoSched()
		}
		casgstatus(gp, _Grunning, _Grunnable)
		dropg()
		lock(&sched.lock)
		globrunqput(gp)
		unlock(&sched.lock)

		if trace.enabled {
			traceGoUnpark(callingG, 0)
		}
		casgstatus(callingG, _Gwaiting, _Grunnable)
		execute(callingG, true)
	})
}

func debugCallWrap2(dispatch uintptr) {
	// 调用分派函数并捕获恐慌。
	var dispatchF func()
	dispatchFV := funcval{dispatch}
	*(*unsafe.Pointer)(unsafe.Pointer(&dispatchF)) = noescape(unsafe.Pointer(&dispatchFV))

	var ok bool
	defer func() {
		if !ok {
			err := recover()
			debugCallPanicked(err)
		}
	}()
	dispatchF()
	ok = true
}
